// Write C++ code here.
//
// Do not forget to dynamically load the C++ library into your application.
//
// For instance,
//
// In MainActivity.java:
//    static {
//       System.loadLibrary("rtmppush");
//    }
//
// Or, in MainActivity.kt:
//    companion object {
//      init {
//         System.loadLibrary("rtmppush")
//      }
//    }
#include <jni.h>
#include <string>

extern "C" {
#include "librtmp/rtmp.h"
}

#include <android/log.h>

#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,"rtmp",__VA_ARGS__)

#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,"rtmp",__VA_ARGS__)

#define  PACKET_AUDIO_TYPE  0;
#define PACKET_VIDEO_TYPE  1;


typedef struct {
    int8_t *sps;
    int16_t sps_len;
    int8_t *pps;
    int16_t pps_len;
    RTMP *rtmp;
}LiveHeader;

LiveHeader *live = nullptr;
//void sendVideo(const int8_t *data, jint len, jlong tms);
//
//void prepareVideo(const int8_t *data, jint len,LiveHeader *liveHeader);

void prepareVideo(const int8_t *data, int len, LiveHeader *live);

RTMPPacket* createSpsAndPpsPacket(LiveHeader *live);

int sendPacket(RTMPPacket *pPacket);

RTMPPacket *createVideoPacket(const int8_t *data, int len, jlong tms, LiveHeader *live);

extern "C"
JNIEXPORT jboolean JNICALL
Java_com_example_rtmppush_ScreenLive_connect(JNIEnv *env, jobject thiz, jstring live_url) {
    // TODO: implement connect()

    live = static_cast<LiveHeader *>(malloc(sizeof(LiveHeader)));
    // todo 初始化 live内存块的值 为 0
    memset(live, 0 ,sizeof (LiveHeader));
    const char *url = env->GetStringUTFChars(live_url, nullptr);
    int ret =0;
    int tryNum = 3;// 失败重试3次
    do {
        LOGE("RTMP_链接中 次数：%d",tryNum);
         live->rtmp = RTMP_Alloc();
        RTMP_Init(live->rtmp);
        LOGE("RTMP_Init");
        // default 30s
        live->rtmp->Link.timeout = 5;

        if (!(ret = RTMP_SetupURL(live->rtmp, (char *) url))) {
            // 失败
            LOGE("RTMP_SetupURL fail");
            RTMP_Free(live->rtmp);
            if (tryNum >0) {
                continue;
            }
            return ret;
        }
        LOGE("RTMP_SetupURL success");
        RTMP_EnableWrite(live->rtmp);
        if (!(ret = RTMP_Connect(live->rtmp, nullptr))) {
            //失败
            LOGE("RTMP_Connect fail");
            RTMP_Free(live->rtmp);
            if (tryNum >0) {
                continue;
            }
            return ret;
        }
        LOGE("RTMP_Connect success");

        if (!(ret = RTMP_ConnectStream(live->rtmp, 0))) {
            //失败
            LOGE("RTMP_ConnectStream---fail:%d",ret);
            RTMP_Close(live->rtmp);
            RTMP_Free(live->rtmp);
            if (tryNum >0) {
                continue;
            }
            return ret;
        }
        LOGE("RTMP_ConnectStream---SUCCESS");
        tryNum= 0;
    } while ((tryNum--)>0);

    env->ReleaseStringUTFChars(live_url, url);
    return ret;

}

int sendVideo(const int8_t *data, int len, jlong tms) {

    LOGE("sendVideo");
    // TODO 1⃣  分理出 pps sps
    int type  = data[4] ;
//    int type  = data[4] ;
    int ret = 0;
    // 0x67 pps  0x68 sps  0x65 I帧

    LOGE("sendVideo ======== %d",type);
    if (type == 0x67) {
        if (live && (!live->sps || !live->pps)){
            prepareVideo(data,len,live);
        }
        LOGE("分理出 pps sps");
        //todo  2⃣  关键帧
    } else {
        if (data[4] == 0x65) { // 关键帧开始
            //todo  3⃣  组装sps pps
            RTMPPacket*  packet =createSpsAndPpsPacket(live);
            //发送数据包
            if (!(ret = sendPacket(packet))) {
                // 发送失败
                LOGE("发送SPS PPS 数据包---- FAIL");
                return ret;
            }
            LOGE("发送SPS PPS 数据包---- SUCCESS");
        }

        // TODO 4⃣  按照rtmp 组装video 包
        RTMPPacket*  packet =createVideoPacket(data,len,tms,live);
        // TODO 5⃣  发送数据包
        LOGE("数据发送1 %d",packet);
        if (!(ret = sendPacket(packet))) {
            // 发送失败
            LOGE("发送Video数据包---- FAIL");
            return ret;
        }
        LOGE("发送Video数据包---- SUCCESS");
    }

    return  ret;


}

// 组装video数据包
RTMPPacket *createVideoPacket(const int8_t *data, int len, jlong tms,
                              LiveHeader *live) {
    RTMPPacket * packet = static_cast<RTMPPacket *>(malloc(sizeof(RTMPPacket)));

    data = data+4;// 跳过 分隔符
    len -= 4;
    int body_size = 9 + len;
    RTMPPacket_Alloc(packet,body_size);

    int type  = data[0] ;
    // I 帧
    if ( type == 0x65) {
        LOGE("组装   I     帧");
        packet->m_body[0] = 0x17;
    } else {
        packet->m_body[0] = 0x27;
        LOGE("组装其他帧");
    }
    packet->m_body[1] = 0x01;
    packet->m_body[2] = 0x00;
    packet->m_body[3] = 0x00;
    packet->m_body[4] = 0x00;

    // 4个字节长度  32位
    packet->m_body[5] = (len >> 24) & 0xFF;
    packet->m_body[6] = (len >> 16) & 0xFF;
    packet->m_body[7] = (len >> 8) & 0xFF;
    packet->m_body[8] = (len) & 0xFF;

    // cpy数据进去
    memcpy(&packet->m_body[9], data, len);

    packet->m_packetType = RTMP_PACKET_TYPE_VIDEO;
    packet->m_nBodySize = body_size;
    packet->m_nChannel = 0x04;
    packet->m_hasAbsTimestamp =tms;

    //系统赋值
    packet->m_nTimeStamp = tms;

    //数据包大小 给服务器看
    packet->m_headerType = RTMP_PACKET_SIZE_LARGE;
    packet->m_nInfoField2 = live->rtmp->m_stream_id;

    return packet;
}

// 发送包
int sendPacket(RTMPPacket *pPacket) {
    LOGE("数据发送 %d",pPacket);
    int ret = 0;
    if (!RTMP_IsConnected(live->rtmp)) {
        LOGE("数据发送 RTMP掉线");
        if (!(ret = RTMP_Connect(live->rtmp, nullptr))) {
            //失败
            LOGE("RTMP_Connect fail");
            RTMP_Free(live->rtmp);
            return ret;
        }
        LOGE("RTMP_Connect success");
        if (!(ret = RTMP_ConnectStream(live->rtmp, 0))) {
            LOGE("RTMP_ConnectStream fail");
            return ret;
        }
        LOGE("RTMP_ConnectStream success");
    }
    LOGE("RTMP 掉线 %d",RTMP_IsConnected(live->rtmp));
   int r = RTMP_SendPacket(live->rtmp,pPacket,1);
    if (!r) {
        LOGE("数据发送失败");
    }
    LOGE("数据发送 RTMP_SendPacket");
    RTMPPacket_Free(pPacket);
    free(pPacket);
    pPacket =nullptr;
    return r;
}

// 创建rtmp 数据包
RTMPPacket* createSpsAndPpsPacket(LiveHeader *live) {

    if (!live->sps || !live->pps) {
        return nullptr;
    }
    RTMPPacket* rtmpPacket = static_cast<RTMPPacket *>(malloc(sizeof(RTMPPacket)));
    // pps +sps 包大小
    int body_size = 16 + live->sps_len + live->pps_len;
    LOGE("live->sps --- body_size%d",body_size);
    // 分配空间
    RTMPPacket_Alloc(rtmpPacket,body_size);

    int index =0;

    rtmpPacket->m_body[index ++] = 0x17;

    rtmpPacket->m_body[index ++] = 0x00;
    rtmpPacket->m_body[index ++] = 0x00;
    rtmpPacket->m_body[index ++] = 0x00;
    rtmpPacket->m_body[index ++] = 0x00;

    rtmpPacket->m_body[index ++] = 0x01;
    rtmpPacket->m_body[index ++] = live->sps[1];
    rtmpPacket->m_body[index ++] = live->sps[2];
    rtmpPacket->m_body[index ++] = live->sps[3];

    //固定写法
    rtmpPacket->m_body[index ++] = 0xFF;
    rtmpPacket->m_body[index ++] = 0xE1;
    //sps长度 2个字节 16位
    rtmpPacket->m_body[index ++] = (live->sps_len >> 8) & 0xFF; // 取出高8位
    rtmpPacket->m_body[index ++] = (live->sps_len ) & 0xFF; // 低8位
    // 整个sps内容 没有直接index++ 方便后面做偏移
    memcpy(&rtmpPacket->m_body[index], live->sps, live->sps_len);
    index  = index + live->sps_len;
    LOGE("live->pps %s",live->pps);
    //  //pps  个数
    rtmpPacket->m_body[index ++] = 0x01;
    // 长度 2个字节
    rtmpPacket->m_body[index ++] = (live->pps_len >> 8) & 0xFF;
    rtmpPacket->m_body[index ++] = (live->pps_len ) & 0xFF;
    memcpy(&rtmpPacket->m_body[index], live->pps, live->pps_len);

    // 拼接成功 配置直播
    rtmpPacket->m_packetType = RTMP_PACKET_TYPE_VIDEO;
    rtmpPacket->m_nBodySize = body_size;
    rtmpPacket->m_nChannel = 0x04;
    rtmpPacket->m_hasAbsTimestamp = 0;

    // 系统赋值
    rtmpPacket->m_nTimeStamp = 0;

    rtmpPacket->m_headerType = RTMP_PACKET_SIZE_LARGE;
    rtmpPacket->m_nInfoField2 = live->rtmp->m_stream_id;
    LOGE("createSpsAndPpsPacket %d",rtmpPacket);
    return rtmpPacket;
}

void prepareVideo(const int8_t *data, int len, LiveHeader *liveHeader) {

//  准备video  解析sps 和pps 赋值给结构体 liveHeader

    int i =0;
    for ( i= 0;  i<len ; i++) {
        if (data[i]==0x00 && data[i+1]== 0x00
            && data[i+2] == 0x00 && data[i+3] == 0x01) {
            // 分隔符
            // pps
            LOGE("PPS=====%d",data[i+4]);
            if ((data[i+4]) == 0x68) {
                liveHeader->sps_len = i-4;
                liveHeader->sps = static_cast<int8_t *>(malloc(liveHeader->sps_len));
                memcpy(liveHeader->sps,data+4,liveHeader->sps_len);

                // pps
                liveHeader ->pps_len = len - (4 + liveHeader->sps_len)-4;
                liveHeader->pps = static_cast<int8_t *>(malloc(liveHeader->pps_len));
                memcpy(liveHeader->pps, data+4+liveHeader->sps_len+4,liveHeader->pps_len );
                // 找到了 就break
                LOGE("SPS  PPS 组装成功");
                break;
            }
        }
    }

}

void release() {
    if (live) {
        RTMP_Free(live->rtmp);
        delete live;
        live = nullptr;
    }
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_example_rtmppush_ScreenLive_sendData(JNIEnv *env, jobject thiz,
                                              jbyteArray data, jint len,
                                              jlong tms, jint type) {
    // TODO: 发送数据

   int8_t *byteData = env->GetByteArrayElements(data, nullptr);
    switch (type) {
        case 0:
            //            sendAudio()
            break;
        case 1:
            sendVideo(byteData, len, tms);
            break;
        default:break;
    }
   /**
    * 0: Updates to the array from within the C code are reflected in the Java language copy.
     JNI_COMMIT: The Java language copy is updated, but the local jbyteArray is not freed.
     JNI_ABORT: Changes are not copied back, but the jbyteArray is freed.
     The value is used only if the array is obtained with a get mode of JNI_TRUE meaning the array is a copy.
     小心最后一个参数,如果为0是会释放 m 所指向的内存的. 如果M刚好指向一个栈上的数组的话,这样可能在Release 版本中造成内存方面的随机错误.可以用JNI_COMMIT来避免.
    */
   env->ReleaseByteArrayElements(data,byteData,JNI_COMMIT);
    return true;
}